package Proxy

import (
	"Volta/Config"
	"Volta/Core/Container"
	"fmt"
	"github.com/rs/zerolog"
	"net"
	"os"
	"reflect"
	"runtime/debug"
)
type TcpProxy struct {

}

var logger = zerolog.New(os.Stdout).With().Timestamp().Logger()

func (tcpProxy *TcpProxy) Handle() {
	bind := Config.Server.TcpProxyHost
	backend := Config.Server.TcpProxyToHost
	success, err := RunProxy(bind, backend)
	if !success {
		logger.Error().Err(err).Send()
		os.Exit(1)
	}
}

func RunProxy(bind, backend string) (bool, error) {
	listener, err := net.Listen("tcp", bind)
	if err != nil {
		return false, err
	}
	defer listener.Close()
	for {
		conn, err := listener.Accept()
		if err != nil {
			logger.Error().Err(err).Send()
		} else {
			go ConnectionHandler(conn, backend)
		}
	}
}

func ConnectionHandler(conn net.Conn, backend string) {
	logger.Info().Str("conn", conn.RemoteAddr().String()).Msg("client connected.")
	target, err := net.Dial("tcp", backend)
	defer conn.Close()
	if err != nil {
		logger.Error().Err(err).Send()
	} else {
		defer target.Close()
		logger.Info().Str("conn", conn.RemoteAddr().String()).Str("backend", target.LocalAddr().String()).Msg("backend connected.")
		closed := make(chan bool, 2)
		go Proxy(conn, target, closed)
		go Proxy(target, conn, closed)
		<-closed
		logger.Info().Str("conn", conn.RemoteAddr().String()).Msg("Connection closed.")
	}
}

func Proxy(from net.Conn, to net.Conn, closed chan bool) {
	buffer := make([]byte, 4096)
	for {
		n1, err := from.Read(buffer)
		if err != nil {
			closed <- true
			return
		}
		n2, err := to.Write(buffer[:n1])
		logger.Debug().Str("from", from.RemoteAddr().String()).Int("recv", n1).Str("to", to.RemoteAddr().String()).Int("send", n2).Send()
		if err != nil {
			closed <- true
			return
		}
	}
}

func Execute() func(appNm string){
	return func(appNm string){
		instance, err := Container.App.Make(appNm)
		if err != nil {
			return
		}
		method := instance.ConValue.MethodByName(instance.Method)
		if !method.IsValid() {
			return
		}
		go begin(func() {
			defer endStack(appNm)
			method.Call([]reflect.Value{})
		})
	}
}
func begin(work func())  {
	work()
}

func endStack(appNm string){
	if err := recover(); err != nil {
		//主体等信息+程序的堆栈信息
		msg := map[string]interface{}{
			"error":   err,                   		  //真正的错误信息
			"application": appNm,   				  //错误服务名
			"stack":   string(debug.Stack()), 		  //此刻程序的堆栈信息
		}
		fmt.Println(msg)
	}
}